
/*
    定时任务的设计
*/
#include <iostream>
#include <stdint.h>
#include <iostream>
#include <functional>
#include <vector>
#include <memory>
#include <unordered_map>

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;

class TimerTask
{
private:
    uint64_t _id;      // 定时器任务的唯一表示符
    uint32_t _timeout; // 定时器任务的超时时间

    TaskFunc _task_cb;    // 定时器任务对象需要执行的任务
    ReleaseFunc _release; // 删除TimeWheel中的对象信息
    bool _canceled;  // false 表示没有被取消，true表示被取消了
public:
    TimerTask(uint64_t id, uint32_t time, const TaskFunc &cb)
        : _id(id), _timeout(time), _task_cb(cb),_canceled(false)
    {
    }
    ~TimerTask()
    {
        // 在这里执行cb
        if(_canceled == false)
        {
            _task_cb();
        }
        _release();
    }
    void SetRelease(const ReleaseFunc &cb)
    {
        _release = cb;
    }
    uint32_t DelayTime()
    {
        return _timeout;
    }
    void Cancel()
    {
        _canceled = true;
    }
};

/*
    时间轮设计
*/

class TimerWheel
{
    using PtrTask = std::shared_ptr<TimerTask>;
    using WeakTask = std::weak_ptr<TimerTask>;  //使用weakptr对于原始TimeTask任务进行管理

private:
    int _tick;
    int _capacity;
    std::vector<std::vector<PtrTask>> _wheel;

    std::unordered_map<uint64_t, WeakTask> _timers;
private:
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it != _timers.end())
            _timers.erase(id);
    }
public:
    TimerWheel()
        : _capacity(60), _tick(0),_wheel(_capacity)
    {
    }
    void TimerADD(uint64_t id, uint32_t time, const TaskFunc &cb)
    {
        PtrTask pt(new TimerTask(id, time, cb)); // 注意用法
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
        /*
            这里不能直接传入RemoverTimer函数，因为这个函数是在类中的，传过去需要知道是哪个对象要执行该函数
            所以为了区分对象，需要传入this指针，并且bind同时也传入了id。
            同时不能设置为RemoveTimer(this, id)，因为SetRelease需要的是一个可被调用的对象
        */
       _wheel[(_tick + time) % _capacity].push_back(pt);
       _timers[id] = WeakTask(pt);
       /*如果用Shared_ptr来保存的话，就会导致在时间轮内的TimeTask中的计数器永远无法减到 0*/
       
       
    }
    void TimerReflash(uint64_t id)
    {
        /*通过保存的weak_ptr 找到需要延时的任务，再创建一个shared_ptr使得计数器 +1，定时时间刷新一次 */
        auto it = _timers.find(id);
        if(it == _timers.end())
        {
            return; //没找到 也就没必要刷新
        }
        PtrTask pt = it->second.lock(); //lock获取weakptr管理的shred ptr
        int delay = pt->DelayTime();
        _wheel[(_tick + delay) % _capacity].push_back(pt);
    }

    //每秒被执行一次， 秒针
    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear(); //每走一步，将这一步内的TimeTask清空
    }
    void TimerCancel(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it != _timers.end())
        {
            PtrTask pt = it->second.lock();
            if(pt) pt->Cancel();

        }
    }
};


#include <unistd.h>
class Test
{
    public :
        Test()
        {
            std::cout << "构造" << std::endl;
        }
        ~Test(){std::cout << "析构" << std::endl; }

};
void DelTest(Test* t)
{
    delete t;
}
int main()
{
    TimerWheel wheel;

    Test *t = new Test();

    wheel.TimerADD(888, 5, std::bind(DelTest, t));
    for(int i = 0; i < 5; i++)
    {
        sleep(1);
        wheel.TimerReflash(888); //刷新定时任务

        wheel.RunTimerTask(); //启动秒针
        std::cout << "刷新并启动指针" << std::endl;
    }
    wheel.TimerCancel(888);
    while(1)
    {
        sleep(1);
        std::cout << "==========================" << std::endl;
        wheel.RunTimerTask();
    }
    return 0;
}